/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

/**
 * 
 */
package hbi.wire.java;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;

public class WireAccepter extends Thread
{

    private final DomainBoundary boundary;

    public WireAccepter(DomainBoundary boundary)
    {
        super("HBI Socket Wire Accepter");
        super.setDaemon(true);

        this.boundary = boundary;
    }

    private final ConcurrentLinkedQueue<InetSocketAddress> addrs = //
    new ConcurrentLinkedQueue<InetSocketAddress>();

    private volatile Selector selector = null;

    public void listen(InetSocketAddress localAddr)
    {
        this.addrs.offer(localAddr);
        if (this.selector != null) this.selector.wakeup();
        if (!this.isAlive()) this.start();
    }

    public void run()
    {
        try
        {
            Selector selector = this.selector = Selector.open();

            while (!Thread.interrupted())
            {
                for (InetSocketAddress addr = addrs.poll(); addr != null; // 
                addr = addrs.poll())
                {
                    try
                    {
                        ServerSocketChannel listenChannel = //
                        ServerSocketChannel.open();
                        listenChannel.configureBlocking(false);
                        listenChannel.socket().bind(addr);
                        listenChannel.register(selector, //
                            SelectionKey.OP_ACCEPT);
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }

                int readyChannels = selector.select();

                if (readyChannels > 0)
                    for (Iterator<SelectionKey> kIt = selector.selectedKeys()
                        .iterator(); kIt.hasNext();)
                    {
                        SelectionKey selKey = kIt.next();
                        kIt.remove();
                        if (!selKey.isValid()) continue;

                        if (selKey.isAcceptable())
                        {
                            try
                            {
                                SocketChannel ch = ((ServerSocketChannel) selKey
                                    .channel()).accept();
                                ch.configureBlocking(false);
                                ch.register(selector, SelectionKey.OP_READ
                                    | SelectionKey.OP_WRITE, new PassiveHand(
                                    this.boundary));
                            }
                            catch (IOException e)
                            {
                                e.printStackTrace();
                            }
                            continue;
                        }

                        try
                        {
                            PassiveHand dhs = (PassiveHand) selKey.attachment();
                            SocketChannel ch = (SocketChannel) selKey.channel();

                            if (selKey.isWritable()) dhs.writeChannel(selKey);
                            if (!ch.isOpen()) continue;
                            if (selKey.isReadable()) dhs.readChannel(selKey);
                            if (!ch.isOpen()) continue;

                            if (dhs.isFinished() && ch.isOpen())
                            {
                                System.err.println("Remote domain '"
                                    + dhs.remoteDomainType.getCanonicalName()
                                    + "/" + dhs.remoteControllerVersion
                                    + "' connected from "
                                    + ch.socket().getRemoteSocketAddress());
                                System.err.println("Local domain '"
                                    + dhs.localDomain.getClass()
                                        .getCanonicalName() + "/"
                                    + dhs.localDomain.getControllerVersion()
                                    + "' engaged.");
                                selKey.cancel();
                                this.boundary.wireConnected(ch, dhs);
                            }
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                            selKey.channel().close();
                        }
                    }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (selector != null)
            {
                for (SelectionKey selKey : selector.keys())
                {
                    try
                    {
                        if (selKey.channel().isOpen())
                            selKey.channel().close();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
                try
                {
                    selector.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

}
